/*
 * chan.hpp
 *
 *  Created on: 2016-3-7
 *      Author: ZhangXiyin
 */

#ifndef EYAS_SOURCES_PROCESS_CHAN_HPP_
#define EYAS_SOURCES_PROCESS_CHAN_HPP_

#include <queue>
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/noncopyable.hpp>

namespace eyas{
namespace process{
template<typename T>
class chan_impl : public boost::noncopyable{
public:
	chan_impl():enable_(true){}
	virtual ~chan_impl(){
		boost::mutex::scoped_lock lock(mutex_);
		enable_ = false;
		while(this->objQueue_.size() > 0){
			this->objQueue_.pop();
		}
		this->condition_.notify_all();
	}
public:
	typedef std::queue<T> obj_queue;
	obj_queue objQueue_;
	boost::mutex mutex_;
	boost::condition_variable condition_;
	bool enable_;
public:
	bool push(const T& obj);
	bool push(EYAS_PROCESS_RV_REF(T) obj);
	bool pop(T& obj);
};
template<typename T>
bool chan_impl<T>::push(const T& obj){
	boost::mutex::scoped_lock lock(this->mutex_);
	if(this->enable_){
		this->objQueue_.push(obj);
		this->condition_.notify_one();
		return true;
	}
	return false;
}
template<typename T>
bool chan_impl<T>::push(EYAS_PROCESS_RV_REF(T) obj){
	boost::mutex::scoped_lock lock(this->mutex_);
	if(this->enable_){
		this->objQueue_.push(obj);
		this->condition_.notify_one();
		return true;
	}
	return false;
}
template<typename T>
bool chan_impl<T>::pop(T& obj){
	boost::mutex::scoped_lock lock(this->mutex_);
	if(!this->enable_){
		return false;
	}
	if(this->objQueue_.size() <= 0){
		this->condition_.wait(lock);
		if(!this->enable_){
			return false;
		}
	}
	if(this->objQueue_.size() > 0){
		obj = this->objQueue_.front();
		this->objQueue_.pop();
		return true;
	}
	return false;
}

template<typename T>
class chan{
public:
	chan(const chan& ch);
	chan(EYAS_PROCESS_RV_REF(chan) ch);
	chan(boost::shared_ptr<chan_impl<T>> chanPtr);
	~chan();
private:
	boost::shared_ptr<chan_impl<T>> chanPtr_;
public:
public:
	bool push(const T& obj){return this->chanPtr_->push(obj);}
	bool push(EYAS_PROCESS_RV_REF(T) obj){return this->chanPtr_->push(obj);}
	bool pop(T& obj){return this->chanPtr_->pop(obj);}

	chan& operator=(const chan& ch);
	chan& operator=(EYAS_PROCESS_RV_REF(chan) ch);
};
template<typename T>
chan<T>::chan(const chan<T>& ch)
:chanPtr_(ch.chanPtr_){
}
template<typename T>
chan<T>::chan(EYAS_PROCESS_RV_REF(chan) ch)
:chanPtr_(ch.chanPtr_){
	ch.chanPtr_ = nullptr;
}
template<typename T>
chan<T>::chan(boost::shared_ptr<chan_impl<T>> chanPtr)
:chanPtr_(chanPtr){
}
template<typename T>
chan<T>::~chan(){
}
template<typename T>
chan<T>& chan<T>::operator=(const chan<T>& ch){
	chanPtr_ = ch.chanPtr_;
	return *this;
}
template<typename T>
chan<T>& chan<T>::operator=(EYAS_PROCESS_RV_REF(chan<T>) ch){
#ifdef EYAS_ENABLE_C11
	chanPtr_ = std::move(ch.chanPtr_);
#else
	chanPtr_ = ch.chanPtr_;
#endif
	ch.chanPtr_ = nullptr;
	return *this;
}

template<typename T>
bool operator<< (chan<T>& ch, const T& obj){
	return ch.push(obj);
}

template<typename T>
bool operator<< (chan<T>& ch, EYAS_PROCESS_RV_REF(T) obj){
	return ch.push(obj);
}

template<typename T>
bool operator>> (chan<T>& ch, T& obj){
	return ch.pop(obj);
}


}
}
#endif /* EYAS_SOURCES_PROCESS_CHAN_HPP_ */
